package com.ruoyi.backend1.utils;

import com.ruoyi.backend1.domain.Source;
import com.ruoyi.backend1.domain.Target;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.InvalidParameterException;
import java.util.HashMap;
import java.util.Map;

public class ObjectUtil {

    public static void main(String[] args) {
        // 创建源对象
        Source source = new Source();
        source.setName("Test");
        source.setAge(25);
        source.setFriends(new String[]{"Alice", "Bob"});

        // 创建目标对象
        Target target = new Target();

        // 属性名称数组
        String[] properties = {"name", "age"};

        // 复制属性
        ObjectUtil.copyProperties(source, target, properties);

        // 输出结果
        System.out.println("Target name: " + target.getName());
        System.out.println("Target age: " + target.getAge());

        // 测试异常情况
        try {
            ObjectUtil.copyProperties(source, target, new String[]{});
        } catch (java.security.InvalidParameterException e) {
            System.out.println(e.getMessage());
        }

        try {
            ObjectUtil.copyProperties(source, target, new String[]{"nonExistentProperty"});
        } catch (RuntimeException e) {
            System.out.println(e.getMessage());
        }

        try {
            ObjectUtil.copyProperties(source, target, new String[]{"friends"});
        } catch (RuntimeException e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * 将source实例的指定属性，赋值给target实例
     * 1. 当properties参数为空数组时，throw java.security.InvalidParameterException 异常。
     * 2. source和target Object 可能是不同类型的class，但他们存在同名同类型的属性，就能进行拷贝。
     * 3. 当properties参数中的属性，在source和target中同名但类型不一致时，throw RuntimeException 异常。
     * 4. source Object的属性，如果是数组、对象等，需要进行深拷贝，即修改target的值不会同步修改source的值。
     * 5. 只能使用 java.lang.reflect包下面的类或函数实现，不能使用第三方的库和函数，包括spring boot的。
     * 6. 请补充各种异常情况和正常情况的测试代码，能处理循环引用等复杂情况者更佳。
     * @param source
     * @param target
     * @param properties
     */
    public static void copyProperties(Object source, Object target, String[] properties) {
        // TODO: Implement this method
        if (properties == null ||properties.length == 0){
            throw new InvalidParameterException("没有属性！");
        }
        // 获取对象属性
        Map<String, Field> sourceFields = getFieldMap(source.getClass());
        Map<String, Field> targetFields = getFieldMap(target.getClass());
        for (String propertyName : properties) {
            Field sourceField = sourceFields.get(propertyName);
            Field targetField = targetFields.get(propertyName);

            if (sourceField == null || targetField == null) {
                throw new RuntimeException("属性不存在！");
            }

            if (sourceField.getType() != targetField.getType()) {
                throw new RuntimeException("属性类型不一致！");
            }
            // 拿到get和set方法
            String getterName = "get" + capitalizeFirstLetter(propertyName);
            String setterName = "set" + capitalizeFirstLetter(propertyName);

            try {
                Method getter  = source.getClass().getMethod(getterName);
                Method setter  = target.getClass().getMethod(setterName, sourceField.getType());

                Object value = getter.invoke(source);
                if (value instanceof Object[] || value instanceof Cloneable) {

                    value = deepCopy(value);
                }
                setter.invoke(target, value);

            } catch (Exception e) {
                throw new RuntimeException(e);
            }

        }

    }
    private static Object deepCopy(Object value) throws Exception {
        if (value instanceof Object[]) {
            Object[] array = (Object[]) value;
            Object[] newArray = (Object[]) java.lang.reflect.Array.newInstance(array.getClass().getComponentType(), array.length);
            System.arraycopy(array, 0, newArray, 0, array.length);
            return newArray;
        } else if (value instanceof Cloneable) {
            Method cloneMethod = value.getClass().getMethod("clone");
            return cloneMethod.invoke(value);
        }
        throw new RuntimeException("Unsupported type for deep copy.");
    }

    private static String capitalizeFirstLetter(String s) {
        if (s == null || s.isEmpty()) {
            return s;
        }
        return s.substring(0, 1).toUpperCase() + s.substring(1);
    }

    private static Map<String, Field> getFieldMap(Class<?> clazz) {
        Map<String, Field> fieldMap = new HashMap<>();
        Class<?> currentClass = clazz;
        while (currentClass != Object.class) {
            for (Field field : currentClass.getDeclaredFields()) {
                fieldMap.put(field.getName(), field);
            }
            currentClass = currentClass.getSuperclass();
        }
        return fieldMap;
    }
}